Skip to content

feat: harden public API limits, server-only env boundaries, and registry corpus sync#488

Merged
BigSimmo merged 14 commits into
mainfrom
claude/code-review-42a2c3
Jul 11, 2026
Merged

feat: harden public API limits, server-only env boundaries, and registry corpus sync#488
BigSimmo merged 14 commits into
mainfrom
claude/code-review-42a2c3

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Fail closed on paid anonymous answer rate limits. Caller-controlled User-Agent no longer partitions the anonymous quota key (rotating it minted a fresh paid-answer allowance per request); a global durable anonymous ceiling (anon:answer:global) caps aggregate spend across rotated/spoofed network identities; the answer bucket refuses the per-instance in-memory limiter fallback outside local no-auth mode so limiter outages return 503 before provider generation starts.
  • Enforce the server env boundary. src/lib/env.ts is now import "server-only"; client components use the landed client-env module. Standalone tsx entrypoints route through scripts/run-tsx.mjs (a node:module hook that stubs server-only), vitest aliases it to a stub, and npm run build now ends with a client-bundle secret-surface scan (scripts/check-client-bundle-secrets.mjs) that fails the build if service-role/OpenAI markers reach .next/static or public/.
  • Structured public API errors + privacy-safe logging. jsonError returns message/code/optional requestId alongside error; route/seed error logging goes through safeErrorLogDetails (path/url/secret/email redaction) instead of raw messages, stacks, and owner ids.
  • Search-scope public fast path. Unfiltered public searches no longer enumerate up to 5000 document ids; scope enforcement relies on the retrieval owner sentinel and the SQL-side status='indexed' + retrieval_owner_matches guards (asserted by test).
  • Registry corpus sync is best-effort and re-embed-frugal. Seed/reseed embedding failures degrade to logged warnings instead of 500s (routes still surface seed errors when no rows exist); hash-gated preflight skips unchanged entries, refreshes rows on derived-metadata drift without new OpenAI calls, and rolls back document upserts if the chunk upsert fails.
  • Safer extraction + misc hardening. safeBufferFrom validates base64 image payloads in the PDF extractor; seed-failure handling unified across differentials/medications/registry routes; migration-name references corrected (20260708160001_retrieval_owner_matches_fail_closed).
  • UI polish (mode-home/medication). Tap-target min-h-tap on filter/tab chips, scrollable pill rows with masked overflow, richer medication prompts/capability pills wired to suggested searches, clearer back-link on medication record pages, @critical tags on the core Chromium smokes.
  • CI scope robustness (ported into main's landed system). changedFilesFromRange no longer fails the whole changes job on a force-push with an unreachable base — it falls through to the full-run sentinel.

Reconciliation note: main independently landed evolved versions of the risk-scoped CI system (#454, e8481dc), the phone composer UX (#456/#470), client-env (#457), and the medication filter strip (#477). This branch's overlapping implementations were dropped in favour of main's during the origin/main merge; only the novel hardening above ships.

Verification

  • npm run verify:pr-local — component gates run individually instead (see Notes): verify:cheap ✅ (runtime, action pins, sitemap, type-scale, lint, typecheck, 1520 vitest passed / 1 skipped), format:check ✅, eval:rag:offline ✅ (57 offline contract tests). Local npm run build deliberately skipped: building in the shared checkout corrupts the live dev server (repo-documented hazard); CI's build lane runs build + client-bundle secret scan.
  • npm run verify:ui — Chromium suite against the local dev server: 130/132. During this run one real regression was caught and fixed (this branch's pre-merge phone-layout assertion contradicted main's landed fix(mobile): keep mode-home composer hero-centred on phones and never let it vanish #470 hero-composer UX; aligned to main's assertion, now green). The remaining 2 failures ("newer routed differential context", "mode home heroes identical sizing on mobile") reproduce identically on pure origin/main locally (Windows scrollbar/vw geometry) — environmental, outside this diff, and not in the @critical lane. npm run test:e2e:critical (CI's UI gate): 8/8 passed.
  • npm run eval:retrieval:quality — 36/36, failed_cases=0:
cases=36
document_recall@5=1
content_recall@5=1
top_k_hit_rate=1
mrr@10=0.8148
content_mrr@10=0.9279 (over 36 content-term case(s))
median_latency_ms=2803
p90_latency_ms=10643
force_embedding_case_count=10
force_embedding_failure_count=0
failed_cases=0
latency_failed_cases=0
  • npm run eval:rag -- --limit 15 + eval:quality -- --rag-only — not applicable: no change to answer generation, the synthesis prompt, or answer post-processing (answer-path changes are logging/rate-limit only; retrieval scoping covered by the 36/36 live eval above)
  • npm run check:production-readiness — READY, 5 PASS, project Clinical KB Database (sjrfecxgysukkwxsowpy)
  • npm run check:deployment-readiness — not run locally; no hosting/startup change (build script only appends the post-build secret scan). CI's boot smoke runs on main.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy) — confirmed by production-readiness check
  • Service-role keys and private document access remain server-only — strengthened: server-only env enforcement + client-bundle secret scan + import-chain test (tests/client-secret-surface.test.ts)
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked — no clinical decision-support behavior change (retrieval scope change proven recall-neutral by the live golden eval)

Notes

  • origin/main was merged into this branch (never rebased) to reconcile with the 17 commits that landed since the branch base; conflicts resolved favouring main's landed implementations everywhere both sides built the same feature.
  • Anonymous quota keys are IP-signal-only now; when no trusted proxy header is present all unknown callers deliberately share one conservative quota rather than failing open.
  • The answer bucket's fail-closed behavior also applies to authenticated subjects in production: a durable-limiter outage returns 503 on the answer path by design.

🤖 Generated with Claude Code

BigSimmo and others added 6 commits July 10, 2026 21:54
…aries

- Fail closed on paid anonymous answer rate limits: drop caller-controlled
  User-Agent from quota keys, add a global durable anonymous ceiling, and
  refuse in-memory limiter fallback for the answer bucket outside local dev
- Enforce server-only env.ts with a client-safe public-env module, tsx/vitest
  server-only stubs, and a post-build client-bundle secret scan
- Replace monolithic CI with risk-scoped lanes (changes/static-pr/safety/
  coverage/build/ui-critical/db-replay) behind a single PR required aggregate;
  full-run sentinel exercises every lane and force-push diffs fail open
- Make registry corpus sync best-effort with hash-gated re-embedding that
  still refreshes rows on derived-metadata drift without new OpenAI calls
- Redact structured error logs via safeErrorLogDetails across routes/seeds,
  return typed error codes from jsonError, and skip public scope enumeration
  in favour of the retrieval owner sentinel
- Polish mode-home/medication UI (tap targets, scrollable pill rows, portal
  composer breakpoints) and tag @critical Chromium smokes for the CI lane

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l hardening

Main independently landed evolved versions of the risk-scoped CI system
(#454, e8481dc), the phone composer UX (#456/#470), the client-env module
(#457), and the medication filter strip redesign. Resolution favours main's
landed implementations and keeps this branch's novel work: fail-closed
anonymous answer limits, server-only env enforcement (run-tsx hook + vitest
stub + bundle secret scan), structured jsonError codes, search-scope public
early return, registry corpus best-effort sync, and safe-buffer extraction.
Dropped this branch's superseded duplicates (lib-based ci-change-scope,
pr-local plan, eval-rag-offline.ts) and ported the force-push diff fallback
into main's ci-change-scope.mjs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…test config

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mposer UX

Main's #470 places the composer above the Start here region on phone mode
homes; this branch's pre-merge assertion encoded the superseded layout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@supabase

supabase Bot commented Jul 11, 2026

Copy link
Copy Markdown

Updates to Preview Branch (claude/code-review-42a2c3) ↗︎

Deployments Status Updated
Database Sat, 11 Jul 2026 10:18:42 UTC
Services Sat, 11 Jul 2026 10:18:42 UTC
APIs Sat, 11 Jul 2026 10:18:42 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Sat, 11 Jul 2026 10:18:44 UTC
Migrations Sat, 11 Jul 2026 10:18:46 UTC
Seeding Sat, 11 Jul 2026 10:18:47 UTC
Edge Functions Sat, 11 Jul 2026 10:18:48 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e7f8aace-da8c-4e2b-9e4c-24611b2f7cd1

📥 Commits

Reviewing files that changed from the base of the PR and between 6f702da and 1c51087.

📒 Files selected for processing (7)
  • docs/branch-review-ledger.md
  • src/app/api/answer/stream/route.ts
  • src/app/api/differentials/[slug]/route.ts
  • src/app/globals.css
  • tests/differentials-route.test.ts
  • tests/ui-smoke.spec.ts
  • tests/ui-tools.spec.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/branch-review-ledger.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/api/answer/stream/route.ts
  • src/app/api/differentials/[slug]/route.ts
  • src/app/globals.css
  • tests/ui-smoke.spec.ts

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added clearer medication prescribing prompts, capability shortcuts, and improved navigation labels.
    • Improved mobile browsing with horizontally scrollable shortcut rows and larger touch targets.
    • Added more informative API error responses with stable error codes and request IDs.
  • Bug Fixes

    • Public document searches now correctly identify all public documents when no filters are selected.
    • Improved recovery when registry data synchronization is temporarily unavailable.
    • Anonymous answer limits now enforce both individual and global quotas.
  • Security

    • Added checks to prevent secrets from appearing in client bundles and public assets.
    • Improved protection against sensitive error details appearing in logs or responses.

Walkthrough

This PR updates runtime verification, client-secret detection, API error handling, rate limiting, registry corpus synchronization, search behavior, prescribing UX, operational documentation, and automated coverage.

Changes

Application hardening

Layer / File(s) Summary
Runtime runners and build verification
.prettierignore, AGENTS.md, package.json, scripts/*, vitest.config.mts
Adds client-bundle scanning, server-only TypeScript execution, safer process handling, and CI fallback behavior.
API errors, privacy, rate limits, and seed recovery
src/lib/http.ts, src/lib/api-rate-limit.ts, src/lib/public-api-access.ts, src/lib/safe-buffer.ts, src/app/api/**
Adds stable error envelopes, sanitized logging, fail-closed answer limits, safer binary decoding, and seed retry handling.
Registry corpus and search behavior
src/lib/registry-corpus.ts, src/lib/*-seed.ts, src/lib/citations.ts, src/lib/search-scope.ts
Detects metadata drift, reuses embeddings, standardizes corpus sync, centralizes differential seeding, and updates citation and public-scope resolution.
Prescribing and responsive UI
src/components/**, src/app/globals.css, src/lib/app-modes.ts
Updates readiness checks, prescribing prompts and shortcuts, touch-target sizing, navigation labels, and horizontally scrollable controls.
Operational documentation and drift records
AGENTS.md, docs/*, supabase/drift-allowlist.json
Updates verification, deployment, migration, drift, corpus, latency, and review-record documentation.
Validation and smoke coverage
tests/**
Adds coverage for secret isolation, rate limits, sanitized errors, corpus reconciliation, route recovery, search scope, touch targets, and network-isolated UI flows.

Estimated code review effort: 4 (Complex) | ~60 minutes


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Verification Claims ❌ Error PR summary says “Verification reports passing static, formatting...” without exact command/result wording, violating the requirement. Rewrite summary/notes to use explicit entries like “Ran npm run verify:cheap: passed” or “Not run: reason” for each check.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (9 passed)
Check name Status Explanation
Title check ✅ Passed It clearly summarizes the main hardening changes across API limits, server-only boundaries, and registry sync.
Description check ✅ Passed It follows the template with Summary, Verification, Clinical Governance Preflight, and Notes sections, with substantive detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Generated And Sensitive Files ✅ Passed No committed secrets or banned artifacts found: diff is docs/tests/scripts plus an allowlist JSON, with no tracked node_modules/.next/build/test-results files.
Risky Git Or Deployment Actions ✅ Passed Changed docs add guarded migration/runbook notes and explicit anti-risk guardrails; no new force-push/reset/clean/rebase or ungated deploy instructions.
Supabase Project And Schema Safety ✅ Passed PASS: The diff only updates migration IDs/docs and a drift allowlist; no SQL/RLS/policy changes or stale Supabase project refs were introduced, and prod checks are guarded.
Runtime And Package Manager Integrity ✅ Passed package.json still targets npm@11.17.0/Node 24.x, .npmrc keeps engine-strict, the lockfile remains package-lock.json, and no force/legacy flags or manager switch were introduced.
Api Route Failure Handling ✅ Passed Touched routes catch auth/Zod/provider/seed errors, rate-limit outages fail closed for answer, and best-effort seed syncs retry or return deterministic 404/503/429 responses.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/code-review-42a2c3
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/code-review-42a2c3

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48cabd9b87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/env.ts
coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
tests/private-access-routes.test.ts (1)

441-441: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Widened error assertions (toEqualtoMatchObject) lose exact-shape verification for error envelopes.

This PR's stated goal is sanitized, structured API errors (Line: error logging and HTTP responses use sanitized details and stable codes per PR summary). Switching these ~25 assertions from strict toEqual to toMatchObject is likely needed because the envelope now includes an additional field (e.g. a stable code), but it also means these tests would silently pass if an unexpected/sensitive extra field leaked into the response alongside error. Given the explicit privacy/sanitization focus of this PR, consider asserting the full expected envelope (e.g. { error: "...", code: "..." }) instead of a partial match, so a future regression that adds a leaked field is caught.

Also applies to: 464-464, 657-657, 671-671, 709-709, 751-751, 860-860, 879-879, 936-936, 1289-1289, 1899-1899, 1963-1963, 2208-2208, 2381-2381, 2445-2445, 2463-2463, 2561-2561, 2684-2684, 2713-2713, 2744-2744, 2927-2927, 2958-2958, 3240-3240, 3340-3340, 3893-3893

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/private-access-routes.test.ts` at line 441, Widened error assertions no
longer verify the complete sanitized response envelope. Update the listed
assertions in tests/private-access-routes.test.ts, including the
payload(response) checks, to use strict equality with the expected error message
and stable code fields, matching the structured API error shape and preventing
unexpected fields from being accepted.
tests/client-secret-surface.test.ts (1)

53-85: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Import-chain scan misses dynamic import() calls.

localImports only inspects top-level ImportDeclaration/ExportDeclaration statements. A client module that reaches env.ts via await import("@/lib/env") (or any dynamic import inside a function) would silently bypass serverEnvImportChains(), defeating the purpose of this guard test.

♻️ Suggested addition to also catch dynamic imports
 function localImports(file: string) {
   const cached = importCache.get(file);
   if (cached) return cached;
   const text = readFileSync(file, "utf8");
   const source = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true);
   const specifiers: string[] = [];
-  for (const statement of source.statements) {
-    if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
-      ...
-    }
-  }
+  const visit = (node: ts.Node) => {
+    if (
+      ts.isCallExpression(node) &&
+      node.expression.kind === ts.SyntaxKind.ImportKeyword &&
+      node.arguments[0] &&
+      ts.isStringLiteral(node.arguments[0])
+    ) {
+      specifiers.push(node.arguments[0].text);
+    }
+    ts.forEachChild(node, visit);
+  };
+  for (const statement of source.statements) {
+    if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
+      ...
+    }
+  }
+  visit(source);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/client-secret-surface.test.ts` around lines 53 - 85, The localImports
function only detects static imports and exports, so dynamic import()
dependencies are omitted from the chain scan. Extend its TypeScript AST
traversal to find CallExpressions whose expression is the import keyword,
collect string-literal module specifiers, and resolve them through
resolveLocalImport alongside existing specifiers; ensure nested dynamic imports
are detected, not just top-level statements.
tests/differentials-route.test.ts (1)

93-114: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Console.error assertions verify the mock's own hardcoded log call, not the real registry-corpus logging path.

bestEffortSyncDifferentialRows is fully mocked here and reimplements the console.error("[differentials] registry corpus sync failed", ...) call itself (Line 106-109), duplicating the shape of the real implementation in src/lib/registry-corpus.ts (console.error(\[${scope}] registry corpus sync failed`, safeErrorLogDetails(error))). The assertions at Lines 249-252 and 282-285 therefore just confirm the mock does what it's told, not that the production sanitized-logging path (safeErrorLogDetails) actually produces this shape. If registry-corpus.ts`'s real scope string or sanitization changes, this test won't catch the regression.

Consider exercising the real bestEffortSyncDifferentialRows/shared best-effort helper (mocking only the embedding call, as done in tests/registry-corpus.test.ts) rather than mocking the whole module's error-handling behavior.

Also applies to: 228-286

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/differentials-route.test.ts` around lines 93 - 114, Replace the fully
mocked bestEffortSyncDifferentialRows implementation in mockRuntime with the
real registry-corpus error-handling path, mocking only the embedding dependency
as in registry-corpus.test.ts. Remove the mock’s hardcoded console.error and
return values, then configure the embedding call to fail so assertions at the
affected test cases verify the production safeErrorLogDetails-based sanitized
logging and real scope string.
src/app/api/medications/[slug]/route.ts (1)

114-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the seed-retry pattern into a shared helper.

The catch/log/refetch/throw-only-if-still-missing logic here duplicates the pattern already extracted as fetchOwnerDifferentialRowsWithSeed for the differentials route. Since medication-seed.ts doesn't expose an equivalent wrapper, this shell is hand-rolled inline — and, per the PR objective of unifying seed-failure handling across routes, is likely duplicated again in the sibling routes (differentials/[slug], differentials/presentations/[slug], registry/records/[slug]) not included in this batch.

♻️ Suggested extraction into medication-seed.ts
+export async function fetchOwnerMedicationRowWithSeed(
+  supabase: AdminClient,
+  ownerId: string,
+  slug: string,
+  fetchRecord: () => Promise<MedicationRecordRow | null>,
+): Promise<MedicationRecordRow | null> {
+  let row = await fetchRecord();
+  if (!row) {
+    const { count, error: countError } = await supabase
+      .from("medication_records")
+      .select("id", { count: "exact", head: true })
+      .eq("owner_id", ownerId);
+    if (countError) throw new Error(countError.message);
+    if ((count ?? 0) === 0) {
+      let seedError: unknown = null;
+      try {
+        await ensureMedicationsSeeded(supabase, ownerId);
+      } catch (error) {
+        seedError = error;
+        console.error("[medications] auto-seed failed", safeErrorLogDetails(error));
+      }
+      row = await fetchRecord();
+      if (!row && seedError) throw seedError;
+    }
+  }
+  return row;
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/medications/`[slug]/route.ts around lines 114 - 122, Extract the
medication seed retry behavior into a shared helper in medication-seed.ts,
matching fetchOwnerDifferentialRowsWithSeed: perform the initial fetch, attempt
ensureMedicationsSeeded, log failures with safeErrorLogDetails, refetch
afterward, and rethrow the seed error only when the record remains missing.
Update the medications route and sibling record routes to use this helper
instead of duplicating the inline seedError/catch/refetch logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/database-drift-detection.md`:
- Around line 102-108: Update the document’s “Last updated” header to 2026-07-10
so it matches the newly added update.

In `@docs/operator-apply-july8-batch.md`:
- Around line 24-27: Update the obsolete migration ID references in the R17
warning and manual-repair note to consistently use 20260708160001, including the
reference around the manual repair instructions.

In `@src/components/clinical-dashboard/medication-prescribing-workspace.tsx`:
- Around line 91-94: Update the Safety capability description near the
ShieldCheck configuration from “Avoid and cautions” to “Contraindications and
cautions” (or “Avoidance and cautions”) to provide grammatical user-facing copy.

In `@src/lib/http.ts`:
- Around line 32-39: Harden publicErrorCode so PublicApiError details.code is
only returned when it matches a stable lowercase snake_case identifier;
otherwise fall back to the existing status-based handling, including
internal_error for server failures. Add or update tests covering arbitrary error
class names such as TypeError and valid stable codes.

---

Nitpick comments:
In `@src/app/api/medications/`[slug]/route.ts:
- Around line 114-122: Extract the medication seed retry behavior into a shared
helper in medication-seed.ts, matching fetchOwnerDifferentialRowsWithSeed:
perform the initial fetch, attempt ensureMedicationsSeeded, log failures with
safeErrorLogDetails, refetch afterward, and rethrow the seed error only when the
record remains missing. Update the medications route and sibling record routes
to use this helper instead of duplicating the inline seedError/catch/refetch
logic.

In `@tests/client-secret-surface.test.ts`:
- Around line 53-85: The localImports function only detects static imports and
exports, so dynamic import() dependencies are omitted from the chain scan.
Extend its TypeScript AST traversal to find CallExpressions whose expression is
the import keyword, collect string-literal module specifiers, and resolve them
through resolveLocalImport alongside existing specifiers; ensure nested dynamic
imports are detected, not just top-level statements.

In `@tests/differentials-route.test.ts`:
- Around line 93-114: Replace the fully mocked bestEffortSyncDifferentialRows
implementation in mockRuntime with the real registry-corpus error-handling path,
mocking only the embedding dependency as in registry-corpus.test.ts. Remove the
mock’s hardcoded console.error and return values, then configure the embedding
call to fail so assertions at the affected test cases verify the production
safeErrorLogDetails-based sanitized logging and real scope string.

In `@tests/private-access-routes.test.ts`:
- Line 441: Widened error assertions no longer verify the complete sanitized
response envelope. Update the listed assertions in
tests/private-access-routes.test.ts, including the payload(response) checks, to
use strict equality with the expected error message and stable code fields,
matching the structured API error shape and preventing unexpected fields from
being accepted.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: efa811bd-5bf9-4ee4-a272-69327a7380e5

📥 Commits

Reviewing files that changed from the base of the PR and between dd7012c and 48cabd9.

📒 Files selected for processing (65)
  • .prettierignore
  • AGENTS.md
  • docs/branch-review-ledger.md
  • docs/database-drift-detection.md
  • docs/deployment-architecture.md
  • docs/operator-apply-july8-batch.md
  • docs/rag-hybrid-findings-and-todo.md
  • docs/tenancy-defense-in-depth-review.md
  • package.json
  • scripts/check-client-bundle-secrets.mjs
  • scripts/check-july8-live-batch.ts
  • scripts/check-retrieval-owner-migration.ts
  • scripts/ci-change-scope.mjs
  • scripts/enable-server-only-stub.mjs
  • scripts/register-server-only.mjs
  • scripts/run-eval-safe.mjs
  • scripts/run-tsx.mjs
  • scripts/run-vitest.mjs
  • src/app/api/answer/stream/route.ts
  • src/app/api/differentials/[slug]/route.ts
  • src/app/api/differentials/presentations/[slug]/route.ts
  • src/app/api/differentials/route.ts
  • src/app/api/medications/[slug]/route.ts
  • src/app/api/registry/records/[slug]/route.ts
  • src/app/globals.css
  • src/components/clinical-dashboard/DocumentManagerPanel.tsx
  • src/components/clinical-dashboard/medication-prescribing-workspace.tsx
  • src/components/clinical-dashboard/medication-record-page.tsx
  • src/components/mode-home-template.tsx
  • src/lib/api-rate-limit.ts
  • src/lib/app-modes.ts
  • src/lib/citations.ts
  • src/lib/differential-seed.ts
  • src/lib/env.ts
  • src/lib/extractors/document.ts
  • src/lib/http.ts
  • src/lib/medication-seed.ts
  • src/lib/owner-scope.ts
  • src/lib/public-api-access.ts
  • src/lib/registry-corpus.ts
  • src/lib/registry-seed.ts
  • src/lib/safe-buffer.ts
  • src/lib/search-scope.ts
  • supabase/drift-allowlist.json
  • tests/api-rate-limit-fallback.test.ts
  • tests/api-validation-contract.test.ts
  • tests/client-secret-surface.test.ts
  • tests/differentials-route.test.ts
  • tests/http-error-response.test.ts
  • tests/medications-route.test.ts
  • tests/owner-scope.test.ts
  • tests/private-access-routes.test.ts
  • tests/public-api-access.test.ts
  • tests/registry-corpus.test.ts
  • tests/registry-records-route.test.ts
  • tests/retrieval-owner-filter-guard.test.ts
  • tests/safe-buffer.test.ts
  • tests/search-interaction-route.test.ts
  • tests/search-scope.test.ts
  • tests/stubs/server-only.ts
  • tests/tsx-server-only-runner.test.ts
  • tests/ui-smoke.spec.ts
  • tests/ui-tools.spec.ts
  • tests/worker-safe-logging.test.ts
  • vitest.config.mts

Comment thread docs/database-drift-detection.md
Comment thread docs/operator-apply-july8-batch.md
Comment thread src/lib/http.ts
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 5 file(s) based on 4 unresolved review comments.

Files modified:

  • docs/database-drift-detection.md
  • docs/operator-apply-july8-batch.md
  • src/components/clinical-dashboard/medication-prescribing-workspace.tsx
  • src/lib/http.ts
  • tests/http-error-response.test.ts

Commit: 7a8ea145013444f7cc29631499f48a8b0454937a

The changes have been pushed to the claude/code-review-42a2c3 branch.

Time taken: 5m 21s

@BigSimmo
BigSimmo enabled auto-merge (squash) July 11, 2026 07:45
coderabbitai Bot and others added 4 commits July 11, 2026 07:51
Fixed 5 file(s) based on 4 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/ui-smoke.spec.ts (1)

1930-1940: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant fixed-delay re-assertion.

toContainText already polls/retries internally, so the second identical assertion after waitForTimeout(600) mostly just re-verifies via a hardcoded sleep rather than an event-driven wait. If the intent is to confirm the status doesn't later flip (e.g., due to a delayed async fetch), consider documenting that intent in a comment, or use expect.poll with a timeout tied to the specific async event instead of an arbitrary sleep, to reduce flakiness risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ui-smoke.spec.ts` around lines 1930 - 1940, Replace the fixed
waitForTimeout and redundant second toContainText assertion in the differential
source-status test with an event-driven expect.poll or another wait tied to the
delayed async request. If retaining the second verification to ensure the status
does not change, document that intent and use a bounded, meaningful timeout
rather than a hardcoded sleep.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/ui-smoke.spec.ts`:
- Around line 1930-1940: Replace the fixed waitForTimeout and redundant second
toContainText assertion in the differential source-status test with an
event-driven expect.poll or another wait tied to the delayed async request. If
retaining the second verification to ensure the status does not change, document
that intent and use a bounded, meaningful timeout rather than a hardcoded sleep.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6715b195-ece6-48f5-bb1a-b5baec7128e0

📥 Commits

Reviewing files that changed from the base of the PR and between 48cabd9 and 6f702da.

📒 Files selected for processing (8)
  • docs/branch-review-ledger.md
  • docs/database-drift-detection.md
  • docs/operator-apply-july8-batch.md
  • src/components/clinical-dashboard/medication-prescribing-workspace.tsx
  • src/lib/http.ts
  • tests/http-error-response.test.ts
  • tests/ui-smoke.spec.ts
  • tests/ui-tools.spec.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/operator-apply-july8-batch.md
  • docs/branch-review-ledger.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/database-drift-detection.md
  • tests/http-error-response.test.ts
  • src/lib/http.ts
  • tests/ui-tools.spec.ts
  • src/components/clinical-dashboard/medication-prescribing-workspace.tsx

@BigSimmo
BigSimmo dismissed coderabbitai[bot]’s stale review July 11, 2026 08:58

All actionable threads are resolved on the current head; dismissing the stale blocking review for verified merge readiness.

BigSimmo added 2 commits July 11, 2026 18:14
# Conflicts:
#	docs/branch-review-ledger.md
#	src/app/api/answer/stream/route.ts
#	tests/differentials-route.test.ts
#	tests/ui-tools.spec.ts
@BigSimmo
BigSimmo merged commit b87681f into main Jul 11, 2026
16 of 17 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1c510876b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/run-tsx.mjs
process.exit(1);
}

const tsxCli = fileURLToPath(import.meta.resolve("tsx/cli"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve tsx resolution for secondary worktrees

When an eval/package script runs from a secondary git worktree that relies on a sibling or hoisted node_modules rather than its own install, this direct import.meta.resolve("tsx/cli") throws before any fallback can run. The previous eval runner explicitly walked ancestor and sibling worktrees for the tsx CLI, but those eval scripts now delegate through this new runner, so commands like npm run eval:rag fail before the server-only stub is installed unless every worktree has a local install. Moving the old resolver logic into run-tsx.mjs would keep the new stub behavior without regressing the documented multi-worktree workflow.

Useful? React with 👍 / 👎.

BigSimmo added a commit that referenced this pull request Jul 11, 2026
…runner (#493)

* fix(worker): boot ingestion worker through the server-only-aware tsx runner

Dockerfile.worker ran `node node_modules/tsx/dist/cli.mjs worker/index.ts`
directly. worker/index.ts imports src/lib/env, which begins with
`import "server-only"` (added in #488). Under bare tsx that import throws, so
the production ingestion worker container crash-loops on boot. Route the
entrypoint through scripts/run-tsx.mjs, which registers the server-only stub
hook so the import resolves outside the Next bundler.

Guard it in tests/tsx-server-only-runner.test.ts: the worker CMD must route
through run-tsx.mjs and never invoke bare tsx, so this cannot regress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(worker): assert the exact worker CMD exec vector, not substrings

Addresses CodeRabbit review on #493: parse the JSON exec-form CMD and assert
the full ["node","scripts/run-tsx.mjs","worker/index.ts"] vector so bare-tsx
variants (["tsx", …], ["npx","tsx", …], or reordered args) cannot slip past
substring checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants